#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
华为云OBS上传器 - 文档转换项目专用版本

从.env文件读取配置，支持文件上传到华为云OBS存储。
"""

import os
import uuid
import logging
import asyncio
from typing import Dict, Any, Optional, Union
from pathlib import Path

# 导入环境变量处理
try:
    from dotenv import load_dotenv
    load_dotenv()
except ImportError:
    # 如果没有python-dotenv，直接使用os.environ
    pass

# 导入华为云OBS SDK
try:
    from obs import ObsClient  # type: ignore
    HAS_OBS_SDK = True
except ImportError:
    ObsClient = None
    HAS_OBS_SDK = False

logger = logging.getLogger(__name__)


class OBSUploadError(Exception):
    """OBS上传异常"""
    pass


class ObsUploader:
    """华为云OBS上传器
    
    支持从.env文件读取配置，自动上传文件到华为云OBS存储。
    """
    
    def __init__(self, config: Optional[Dict[str, Any]] = None):
        """
        初始化OBS上传器
        
        Args:
            config: 可选的配置字典，如果不提供则从环境变量读取
        """
        self.obs_config = self._load_config(config)
        self.client = None
        
        # 检查OBS SDK是否可用
        if not HAS_OBS_SDK:
            logger.warning("华为云OBS SDK未安装，请运行: pip install esdk-obs-python")
            return
        
        # 检查配置完整性
        required_keys = ["access_key", "secret_key", "endpoint", "bucket"]
        missing_keys = [key for key in required_keys if not self.obs_config.get(key)]
        
        if missing_keys:
            logger.warning(f"OBS配置不完整，缺少: {', '.join(missing_keys)}。上传功能将不可用")
            return
        
        # 初始化OBS客户端
        try:
            self.client = ObsClient(  # type: ignore
                access_key_id=self.obs_config["access_key"],
                secret_access_key=self.obs_config["secret_key"],
                server=self.obs_config["endpoint"]
            )
            logger.info("OBS客户端初始化成功")
        except Exception as e:
            logger.error(f"OBS客户端初始化失败: {e}")
            raise OBSUploadError(f"OBS客户端初始化失败: {e}")
    
    def _load_config(self, config: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        """
        加载OBS配置
        
        Args:
            config: 可选的配置字典
            
        Returns:
            Dict: OBS配置
        """
        if config:
            return config
        
        # 从环境变量读取配置
        obs_config = {
            "access_key": os.getenv("OBS_ACCESS_KEY", ""),
            "secret_key": os.getenv("OBS_SECRET_KEY", ""),
            "endpoint": os.getenv("OBS_ENDPOINT", ""),
            "bucket": os.getenv("OBS_BUCKET", ""),
            "region": os.getenv("OBS_REGION", "cn-north-4")
        }
        
        return obs_config
    
    def is_available(self) -> bool:
        """检查OBS服务是否可用"""
        return self.client is not None and HAS_OBS_SDK
    
    async def upload_file(
        self,
        file_path: Union[str, Path],
        object_key: Optional[str] = None,
        content_type: Optional[str] = None,
        folder: str = "files"
    ) -> Dict[str, Any]:
        """
        上传文件到OBS
        
        Args:
            file_path: 本地文件路径
            object_key: OBS对象键，如果为None则自动生成
            content_type: 文件MIME类型
            folder: 存储文件夹，默认为"files"
            
        Returns:
            Dict: 上传结果，包含 success, file_url, object_key, error 等字段
        """
        if not self.is_available():
            return {
                "success": False,
                "error": "OBS服务不可用",
                "file_url": None,
                "object_key": None
            }
        
        file_path = Path(file_path)
        if not file_path.exists():
            return {
                "success": False,
                "error": f"文件不存在: {file_path}",
                "file_url": None,
                "object_key": None
            }
        
        try:
            # 生成对象键
            if not object_key:
                filename = file_path.name
                timestamp = int(asyncio.get_event_loop().time() * 1000)
                object_key = f"{folder}/{timestamp}_{filename}"
            
            # 设置上传头部
            headers = {"acl": "public-read"}
            if content_type:
                headers["Content-Type"] = content_type
            
            # 异步上传文件
            loop = asyncio.get_event_loop()
            resp = await loop.run_in_executor(
                None,
                lambda: self.client.putFile(  # type: ignore
                    bucketName=self.obs_config["bucket"],
                    objectKey=object_key,
                    file_path=str(file_path),
                    headers=headers
                )
            )
            
            if resp.status < 300:
                file_url = self.get_file_url(object_key)
                logger.info(f"文件上传成功: {file_url}")
                return {
                    "success": True,
                    "file_url": file_url,
                    "object_key": object_key,
                    "error": None
                }
            else:
                error_msg = f"上传失败: HTTP {resp.status} - {getattr(resp, 'errorMessage', 'Unknown error')}"
                logger.error(error_msg)
                return {
                    "success": False,
                    "error": error_msg,
                    "file_url": None,
                    "object_key": object_key
                }
                
        except Exception as e:
            error_msg = f"上传文件时发生异常: {e}"
            logger.error(error_msg)
            return {
                "success": False,
                "error": error_msg,
                "file_url": None,
                "object_key": object_key
            }
    
    async def upload_bytes(
        self,
        data: bytes,
        filename: str,
        content_type: Optional[str] = None,
        folder: str = "images"
    ) -> Dict[str, Any]:
        """
        上传二进制数据到OBS
        
        Args:
            data: 二进制数据
            filename: 文件名
            content_type: 文件MIME类型
            folder: 存储文件夹，默认为"images"
            
        Returns:
            Dict: 上传结果
        """
        if not self.is_available():
            return {
                "success": False,
                "error": "OBS服务不可用",
                "file_url": None,
                "object_key": None
            }
        
        try:
            # 生成对象键
            timestamp = int(asyncio.get_event_loop().time() * 1000)
            object_key = f"{folder}/{timestamp}_{filename}"
            
            # 设置上传头部
            headers = {"acl": "public-read"}
            if content_type:
                headers["Content-Type"] = content_type
            
            # 异步上传数据
            loop = asyncio.get_event_loop()
            resp = await loop.run_in_executor(
                None,
                lambda: self.client.putObject(  # type: ignore
                    bucketName=self.obs_config["bucket"],
                    objectKey=object_key,
                    content=data,
                    headers=headers
                )
            )
            
            if resp.status < 300:
                file_url = self.get_file_url(object_key)
                logger.info(f"数据上传成功: {file_url}")
                return {
                    "success": True,
                    "file_url": file_url,
                    "object_key": object_key,
                    "error": None
                }
            else:
                error_msg = f"上传失败: HTTP {resp.status} - {getattr(resp, 'errorMessage', 'Unknown error')}"
                logger.error(error_msg)
                return {
                    "success": False,
                    "error": error_msg,
                    "file_url": None,
                    "object_key": object_key
                }
                
        except Exception as e:
            error_msg = f"上传数据时发生异常: {e}"
            logger.error(error_msg)
            return {
                "success": False,
                "error": error_msg,
                "file_url": None,
                "object_key": None
            }
    
    async def upload(self, image: Dict[str, Any]) -> Optional[str]:
        """
        通用图片上传方法，兼容旧版API
        
        Args:
            image: 图片信息字典，支持以下字段:
                - path: 图片文件路径
                - data: 图片二进制数据
                - filename: 文件名
                - format: 图片格式
                
        Returns:
            str: 上传成功返回URL，失败返回None
        """
        try:
            if "data" in image and image["data"]:
                # 上传二进制数据
                filename = image.get("filename", f"{uuid.uuid4().hex}.{image.get('format', 'png')}")
                result = await self.upload_bytes(
                    data=image["data"],
                    filename=filename,
                    content_type=f"image/{image.get('format', 'png')}"
                )
                return result["file_url"] if result["success"] else None
                
            elif "path" in image and os.path.exists(image["path"]):
                # 上传文件
                result = await self.upload_file(
                    file_path=image["path"],
                    folder="images"
                )
                return result["file_url"] if result["success"] else None
            else:
                logger.error("图片数据不完整，需要提供path或data")
                return None
                
        except Exception as e:
            logger.error(f"上传图片失败: {e}")
            return None
    
    async def delete_file(self, object_key: str) -> bool:
        """
        删除OBS中的文件
        
        Args:
            object_key: 对象键
            
        Returns:
            bool: 删除是否成功
        """
        if not self.is_available():
            return False
        
        try:
            loop = asyncio.get_event_loop()
            resp = await loop.run_in_executor(
                None,
                lambda: self.client.deleteObject(  # type: ignore
                    bucketName=self.obs_config["bucket"],
                    objectKey=object_key
                )
            )
            
            if resp.status < 300:
                logger.info(f"文件删除成功: {object_key}")
                return True
            else:
                logger.error(f"文件删除失败: HTTP {resp.status}")
                return False
                
        except Exception as e:
            logger.error(f"删除文件时发生异常: {e}")
            return False
    
    async def file_exists(self, object_key: str) -> bool:
        """
        检查文件是否存在
        
        Args:
            object_key: 对象键
            
        Returns:
            bool: 文件是否存在
        """
        if not self.is_available():
            return False
        
        try:
            loop = asyncio.get_event_loop()
            resp = await loop.run_in_executor(
                None,
                lambda: self.client.getObjectMetadata(  # type: ignore
                    bucketName=self.obs_config["bucket"],
                    objectKey=object_key
                )
            )
            return resp.status < 300
        except Exception:
            return False
    
    def get_file_url(self, object_key: str) -> str:
        """
        获取文件的公共访问URL
        
        Args:
            object_key: 对象键
            
        Returns:
            str: 文件URL
        """
        return f"https://{self.obs_config['bucket']}.{self.obs_config['endpoint']}/{object_key}"
    
    async def close(self):
        """关闭OBS客户端"""
        if self.client:
            try:
                loop = asyncio.get_event_loop()
                await loop.run_in_executor(None, self.client.close)
                logger.info("OBS客户端已关闭")
            except Exception as e:
                logger.warning(f"关闭OBS客户端时出错: {e}")
    
    def __del__(self):
        """析构函数"""
        try:
            if self.client:
                self.client.close()
        except Exception:
            pass
    
    def get_config_status(self) -> Dict[str, Any]:
        """
        获取配置状态信息
        
        Returns:
            Dict: 配置状态
        """
        return {
            "sdk_available": HAS_OBS_SDK,
            "client_initialized": self.client is not None,
            "bucket": self.obs_config.get("bucket", ""),
            "endpoint": self.obs_config.get("endpoint", ""),
            "region": self.obs_config.get("region", ""),
            "has_credentials": bool(self.obs_config.get("access_key") and self.obs_config.get("secret_key"))
        }


# 全局实例（可选）
_global_uploader = None

def get_obs_uploader() -> ObsUploader:
    """获取全局OBS上传器实例"""
    global _global_uploader
    if _global_uploader is None:
        _global_uploader = ObsUploader()
    return _global_uploader 